%{
	#include <stdlib.h>
	#include <stdio.h>
	#include "lp_defs.h"
	#include "abstractnode.h"
	#include "block.h"
	#include "class.h"
	#include "condition.h"
	#include "constant.h"
	#include "expr.h"
	#include "id.h"
	#include "list.h"
	#include "loops.h"
	#include "message.h"
	#include "method.h"
	
	int yyerror(char* s);
	
	extern int yylex(void);

	ListNode<Method> * root;
%}

%locations
%error-verbose

%union
{
	int c_int;
	std::string * c_string;
	
	Message * CMessge;
	Method * CMethod;
	Loop * CLoop;
	LoopToDo * CLoopToDo;
	AbstractNode * CAbstractNode;
	Block * CBlock;
	Condition * CCOndition;
	Constant * CConstant;
	Expression * CExpression;
	VarList * CVarList;
	MethodVarList * CMethodVarList;
	ListNode<Method> * CMethodList;
	ListNode<AbstractNode> * CStmtList;
	Id * CId;
	
}
%type <CMethodList>		program;
%type <CMethodList>		method_list;
%type <CMethod>			method;
%type <CMethodVarList>	var_list;
%type <CMethodVarList>	empty_var_list;
%type <CMethodVarList>	mul_var_list;
%type <CStmtList>		executable_code;
%type <CStmtList>		code;
%type <CStmtList>		stmt_list;
%type <CAbstractNode>	stmt;
%type <CAbstractNode>	stmt_incompleate;
%type <CVarList>		var_def_or_empty
%type <CVarList>		var_def;
%type <CVarList>		id_list;
%type <CCOndition>		cond;
%type <CLoop>			times_repeat;
%type <CLoop>			while_true;
%type <CLoop>			while_false;
%type <CLoopToDo>		to_do;
%type <CBlock>			code_block;
%type <CBlock>			zero_arg_block;
%type <CBlock>			one_arg_block;
%type <CBlock>			two_arg_block;
%type <CExpression>		expr;
%type <CConstant>		constant;
%type <CMessge>			unary_message;
%type <CMessge>			message_param;
%type <CId>				id;
%type <CId>				pseudo_variable_reference;

%token <c_string> ID 
%token <c_string> STRING
%token <c_int> DIGIT

%token TIMESREPEAT
%token IFTRUE
%token IFFALSE
%token WHILETRUE
%token WHILEFALSE
%token TO
%token DO
%token NIL
%token SELF
%token TRUEC
%token FALSEC
%token SUPER
%token OR
%token AND
%token NEW
%token SELECT
%token LESSOREQ
%token GREATOREQ
%token ASSIG
%token NEQ
%token SAME

%right '='
%left '|'
%left '+' '-' '*' '/'
%nonassoc ':'

%%

program: method_list			{$$=root=$1; }
;

method_list: method				{$$=CREATE_LIST(Method); $$->add($1);}
		| method_list method	{$$=$1; $$->add($2);}
;
		
method: var_list executable_code	{$$=new Method($1,$2);}
;

var_list: empty_var_list	{$$=$1;}
		| mul_var_list		{$$=$1;}
;

empty_var_list: expr					{$$=new MethodVarList($1);}
;

mul_var_list: expr ':' id			{$$=new MethodVarList($1,$3);}	
		| mul_var_list expr ':' id	{$$=$1; $$->add($2,$4);}
;

executable_code: '[' var_def_or_empty code ']' {$$=$3; if ($2!=NULL) {$$->push_front($2);}}
;

code: stmt_list stmt_incompleate {$$=$1; $$->add($2);}
	| stmt_incompleate {$$=CREATE_LIST(AbstractNode); $$->add($1);}
;


stmt_list: stmt				{$$=CREATE_LIST(AbstractNode); $$->add($1);}
		| stmt_list stmt	{$$=$1; $$->add($2);}
;

stmt : stmt_incompleate '.'		{$$=$1;}
;

stmt_incompleate: expr			{$$=$1;}
	| cond						{$$=$1;}
	| times_repeat				{$$=$1;}
	| while_true				{$$=$1;}
	| while_false				{$$=$1;}
	| to_do						{$$=$1;}
;

var_def_or_empty:	/*empty*/	{$$=NULL;}
				|	var_def		{$$=$1;}
;

var_def: '|' id_list '|'			{$$=$2;}
;

id_list:  id				{$$=new VarList($1);}	
		| id_list id		{$$=$1; $$->add($2);}
;

cond: expr IFTRUE ':' zero_arg_block IFFALSE ':' zero_arg_block	{$$=new Condition($1,$4,$7);}
	| expr IFTRUE ':' zero_arg_block							{$$=new Condition($1,$4,NULL);}
	| expr IFFALSE ':' zero_arg_block							{$$=new Condition($1,NULL,$4);}
;

times_repeat : expr TIMESREPEAT zero_arg_block				{$$=new Loop(new std::string("timesRepeat"), $1, $3);}
;

while_true : code_block WHILETRUE zero_arg_block			{$$=new Loop(new std::string("whileTrue"), $1, $3);}
;

while_false : code_block WHILEFALSE zero_arg_block			{$$=new Loop(new std::string("whileFalse"), $1, $3);}
;

to_do : expr TO ':' expr DO ':' one_arg_block				{$$=new LoopToDo($1,$4,$7);}
;

code_block : zero_arg_block		{$$=$1;}
		| one_arg_block			{$$=$1;}
		| two_arg_block			{$$=$1;}
;

zero_arg_block : '[' code']'	{$$=new Block(NULL,NULL,$2);}
;

one_arg_block : '[' ':' id '|' code ']'	{$$=new Block($3,NULL,$5);}
;

two_arg_block : '[' ':' id ':' id '|' code ']'	{$$=new Block($3,$5,$7);}
;
	
expr: id				{$$=new Expression($1);}
	| constant			{$$=new Expression($1);}
	| expr '+' expr		{$$=new Expression("+",$1,$3);}
	| expr '-' expr		{$$=new Expression("+",$1,$3);}
	| expr '*' expr		{$$=new Expression("+",$1,$3);}
	| expr '/' expr		{$$=new Expression("+",$1,$3);}
	| expr '=' expr		{$$=new Expression("+",$1,$3);}
	| expr '<' expr		{$$=new Expression("<",$1,$3);}
	| expr '>' expr		{$$=new Expression(">",$1,$3);}
	| expr LESSOREQ expr		{$$=new Expression("<=",$1,$3);}
	| expr GREATOREQ expr		{$$=new Expression(">=",$1,$3);}
	| expr NEQ expr		{$$=new Expression("~=",$1,$3);}
	| expr SAME expr	{$$=new Expression("==",$1,$3);}
	| expr ASSIG expr	{$$=new Expression(":=",$1,$3);}
	| '(' expr ')'		{$$=$2;}
	| '^' expr			{$$=new Expression($2);}
	| unary_message		{$$=new Expression($1);}
	| message_param		{$$=new Expression($1);}
;


                
constant: DIGIT			{$$=new Constant(yylval.c_int);}
	| STRING			{$$=new Constant(yylval.c_string);}
;

unary_message: expr id	{$$=new Message($1,$2);}
;

message_param: expr id ':' expr			{$$=new Message($1,$2,$4);}
		| message_param id ':' expr		{$$=$1; $$->add($2,$4);}
;

id : ID					{$$=new Id(yylval.c_string);}
	| pseudo_variable_reference	{$$=$1;}
;

pseudo_variable_reference: SELF	{$$=new Id(new std::string("self"));}
						| SUPER	{$$=new Id(new std::string("suuper"));}
;

%%
int yyerror(char* s)
{
	printf("Line %d: %s\n", yylloc.first_line, s);
	return -1;
}